iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0

今天我們將在家用品管理 App 的主頁中新增一個側邊欄,來提升 App 的 UX。這個側邊欄不僅能夠方便使用者快速瀏覽不同的物品分類,還可以包含管理、報表、聯絡等選項,幫助使用者更全面地管理物品和帳務。

目標

我們的目標是透過 SwiftUI 建立一個側邊欄 (SlideMenu),讓使用者可以在主頁上輕鬆操作。這個側邊欄將包括以下選項:

  • 管理分類:用來管理物品的不同分類。
  • 管理地點:用來管理物品存放的位置。
  • 帳務報表:提供使用者查看帳務相關的統計資料。
  • 與我聯絡:讓使用者可以快速聯絡 App 的開發者。
    這樣的設計能夠大大提高家用品管理 App 的使用體驗,可以更輕鬆地管理日常生活中的各種物品和相關資訊。

https://ooorito.com/wp-content/uploads/2024/09/%E7%B5%90%E6%9E%9C%E7%A4%BA%E6%84%8F%E5%9C%96.gif

SwiftUI 側邊欄設計

定義 MenuItem

在建立側邊欄的內容之前,我們需要先定義一個 MenuItem 結構。這個結構將代表側邊欄中的每個選單項目,包含文字和圖案。

struct MenuItem: Identifiable {
    var id = UUID()
    let text: String
    let icon: String
}

MenuItem 結構定義了每個選單項目的 id(唯一識別碼)、text(顯示的文字)以及 icon(圖案)。我們會在側邊欄的內容中使用這個結構來表示選單項目。

實作 MenuContent

接著,建立側邊欄(MenuContent)的內容,也就是選單項目,放在 MenuContent 中。這樣的設計可以幫助我們將選單的結構與樣式分開,讓程式碼更加清晰和易於維護。

我們先建立一個名為 MenuContent 的 Swift 檔,並宣告我們需要的選項:

struct MenuContent: View {
    let items: [MenuItem] = [
        MenuItem(text: "管理分類", icon: "square.grid.2x2"),
        MenuItem(text: "管理地點", icon: "location"),
        MenuItem(text: "帳務報表", icon: "chart.pie"),
        MenuItem(text: "給予評分", icon: "star.bubble"),
        MenuItem(text: "與我聯絡", icon: "envelope"),
    ]
    
     var body: some View {
     
     }     
}

接下來要來幫我們的側邊欄製作 UI,可以在 Assets 中建立分別為 BgColor 和 FontColor 的顏色後,在程式碼中指定我們的背景顏色與文字顏色:

https://ooorito.com/wp-content/uploads/2024/09/Assets%E5%BB%BA%E7%AB%8BBgColor.png

var body: some View {
    ZStack {
        Color("BgColor")
        
        VStack(alignment: .leading, spacing: 0) {
            ForEach(items) { item in
                HStack {
                    Button(action: {
                        
                    }, label: {        
                        Image(systemName: item.icon)
                        Text(item.text)
                    })
                    .foregroundColor(Color("FontColor"))
                    .frame(maxWidth: .infinity)
                    .font(.headline)
                    .padding()
                    .overlay {
                        RoundedRectangle(cornerRadius: 15)
                            .stroke(.gray, lineWidth: 2)
                    }
                        
                    
                    Spacer()
                }
                .padding(8)
            }
            
            Spacer()
        }
    }
}

在這段程式碼中,我們使用 ZStack 來設置背景顏色,並且定義側邊欄的每個選項,透過 VStack 將每個按鈕依序排列。ForEach 會迭代 items 陣列中的每個項目,並在畫面上生成對應的按鈕。每個選項都包含一個圖標和文字,設置了點擊動作,並以 RoundedRectangle 做簡單的樣式設定,讓選單看起來更加整齊。

附上完整程式碼:

struct MenuContent: View {
    let items: [MenuItem] = [
        MenuItem(text: "管理分類", icon: "square.grid.2x2"),
        MenuItem(text: "管理地點", icon: "location"),
        MenuItem(text: "帳務報表", icon: "chart.pie"),
        MenuItem(text: "給予評分", icon: "star.bubble"),
        MenuItem(text: "與我聯絡", icon: "envelope"),
    ]
    
    var body: some View {
        ZStack {
            Color("BgColor")
            
            VStack(alignment: .leading, spacing: 0) {
                ForEach(items) { item in
                    HStack {
                        Button(action: {
                            
                        }, label: {        
                            Image(systemName: item.icon)
                            Text(item.text)
                        })
                        .foregroundColor(Color("FontColor"))
                        .frame(maxWidth: .infinity)
                        .font(.headline)
                        .padding()
                        .overlay {
                            RoundedRectangle(cornerRadius: 15)
                                .stroke(.gray, lineWidth: 2)
                        }
                            
                        
                        Spacer()
                    }
                    .padding(8)
                }
                
                Spacer()
            }
        }
    }
}

建立 SlideMenu

接下來,我們需要建立一個名為 SlideMenu 的畫面,這個畫面將負責呈現側邊欄並控制其顯示與隱藏。當 isShowing 為 true 時,側邊欄會從左側滑出,並在背景新增一個半透明的遮罩來強調側邊欄的顯示。

我們先宣告三個變數:isShowing、width、toggleMenu。

  • isShowing:控制灰底是否顯示。
  • width:控制側邊欄的大小。
  • toggleMenu:用來給外部控制是否顯示側邊欄。
struct SlideMenu: View {
    @Binding var isShowing: Bool
    let width: CGFloat
    let toggleMenu: () -> Void
}

接著使用 ZStack 來顯示側邊欄,並建立半透明的背景遮照,且讓它被點擊時需要切換 isShowing 的狀態。因為側邊欄的顯示與隱藏是透過 isShowing 這個綁定的變數來控制的。

var body: some View {
    ZStack {
        if isShowing {
            Rectangle()
                .opacity(0.3)
                .ignoresSafeArea()
                .onTapGesture {
                    isShowing.toggle()
                }
        }
    }
}

我們現在就可以將剛剛新增好的 MenuContent 新增進來,並且新增顯示的動畫:

import SwiftUI

struct SlideMenu: View {
    @Binding var isShowing: Bool
    let width: CGFloat
    let toggleMenu: () -> Void
    
    var body: some View {
        ZStack {
            if isShowing {
                Rectangle()
                    .opacity(0.3)
                    .ignoresSafeArea()
                    .onTapGesture {
                        isShowing.toggle()
                    }
                HStack {
                    VStack(alignment: .leading) {
                        MenuContent()
                        
                        Spacer()
                    }
                    .padding()
                    .frame(width: width, alignment: .leading)
                    .background(Color("BgColor"))
                    
                    Spacer()
                }
                .transition(.move(edge: .leading))
            }
            
            
            
        }
        .animation(.easeInOut, value: isShowing)
    }
}

#Preview {
    SlideMenu(isShowing: .constant(true), width: 370, toggleMenu: {})
}

我們使用 .transition(.move(edge: .leading)) 來讓側邊欄從左側平滑地滑出,增強使用者體驗。關於動畫的詳細說明,我推薦 ChaoCode 的頻道,這一支影片我覺的她介紹的很好 - 動畫的產生 & View 的身份概念 - SwiftUI 新手入門

整合 SlideMenu 到 ContentView

最後,我們要將 SlideMenu 整合到 ContentView,並且透過一個按鈕來控制側邊欄的顯示與隱藏。

import SwiftUI

struct ContentView: View {
    @State private var isShowMenu = false
    
    var body: some View {
        NavigationView {
            ZStack {
                // 主內容
                VStack {
                    // 這裡放入其他 UI 元件
                }
                
                // 將 SlideMenu 加入畫面
                SlideMenu(isShowing: $isShowMenu, width: UIScreen.main.bounds.size.width / 1.5, toggleMenu: toggleMenu)
            }
            .navigationBarHidden(isShowMenu ? true : false)
            .navigationTitle("")
            .navigationBarItems(
                leading: Button(action: {
                    self.isShowMenu.toggle()
                }) {
                    Image(systemName: "line.horizontal.3")
                        .foregroundColor(Color("FontColor"))
                }
            )
            .navigationBarTitleDisplayMode(.inline)
        }
    }
    
    private func toggleMenu() {
        isShowMenu.toggle()
    }
}

在 ContentView 中,我們使用 @State 來追蹤側邊欄的顯示狀態,並透過上方工具列上的按鈕來控制側邊欄的開關。當按鈕被點擊時,側邊欄會滑出或隱藏,讓使用者可以方便地瀏覽不同的頁面。

https://ooorito.com/wp-content/uploads/2024/09/%E7%B5%90%E6%9E%9C%E7%A4%BA%E6%84%8F%E5%9C%96.gif

總結

我們成功在家用品管理 App 中實作一個自訂的側邊欄。這個側邊欄包含了多個選項,未來希望能夠讓使用者更容易地管理分類、地點,以及查看帳務報表等功能。接下來就讓我們一樣一樣實現這些功能吧!明天見囉!


上一篇
Day 13: SwiftUI 編輯項目頁面設計
下一篇
Day 15: SwiftUI 資料設計與初始化
系列文
用 SwiftUI 掌控家庭日用品庫存30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言